package br.com.onlinebidding.dao;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.NoResultException;
import javax.persistence.Persistence;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaQuery;

import br.com.onlinebidding.model.Product;

abstract class GenericDAO<T> implements Serializable {

	private static final long serialVersionUID = 1L;
	private static final EntityManagerFactory emf = Persistence.createEntityManagerFactory("db_onlinebidding");

	@PersistenceContext(unitName = "db_onlinebidding")
	private EntityManager em;
	private final Class<T> entityClass;

	public void beginTransaction() {
		this.em = emf.createEntityManager();
		this.em.getTransaction().begin();
	}

	public void commit() {
		this.em.getTransaction().commit();
	}

	public void rollback() {
		this.em.getTransaction().rollback();
	}

	public void closeTransaction() {
		this.em.close();
	}

	public void commitAndCloseTransaction() {
		commit();
		closeTransaction();
	}

	public void flush() {
		this.em.flush();
	}

	public void joinTransaction() {
		this.em = emf.createEntityManager();
		this.em.joinTransaction();
	}

	public GenericDAO(Class<T> entityClass) {
		this.entityClass = entityClass;
	}

	public void save(T entity) {
		this.em.persist(entity);
	}

	public void delete(T entity) {

		T entityToBeRemoved = (entity);
		this.em.remove(entityToBeRemoved);

	}

	public T update(T entity) {
		return this.em.merge(entity);
	}

	public T find(int entityID) {
		return this.em.find(this.entityClass, entityID);
	}

	public T findReferenceOnly(int entityID) {
		return this.em.getReference(this.entityClass, entityID);
	}
	
	public List<Product> findAllProductsNotUse() {
		return em.createNamedQuery(Product.FIND_BY_PRODUCTS_NOT_USE,
				Product.class).setParameter("inUse", Boolean.FALSE).getResultList();
	}


	@SuppressWarnings({ "unchecked", "rawtypes" })
	public List<T> findAll() {
		CriteriaQuery cq = this.em.getCriteriaBuilder().createQuery();
		cq.select(cq.from(this.entityClass));
		return this.em.createQuery(cq).getResultList();
	}
	
	@SuppressWarnings("unchecked")
	protected T findOneResult(String namedQuery, Map<String, Object> parameters) {
		
		T result = null;

		try {
			Query query = this.em.createNamedQuery(namedQuery);
			if (parameters != null && !parameters.isEmpty()) {
				populateQueryParameters(query, parameters);
			}
			result = (T) query.getSingleResult();
		} catch (NoResultException e) {
			System.out.println("No result found for named query: " + namedQuery);
		} catch (Exception e) {
			System.out.println("Error while running query: " + e.getMessage());
			e.printStackTrace();
		}
		return result;
	}

	private void populateQueryParameters(Query query,Map<String, Object> parameters) {
		for (Entry<String, Object> entry : parameters.entrySet()) {
			query.setParameter(entry.getKey(), entry.getValue());
		}
	}
}
